using System;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using agsXMPP;
using agsXMPP.net;
using agsXMPP.protocol.client;
using Gdk;
using Gtk;
using Mono.Unix;
using Notifications;
using System.Security.Cryptography.X509Certificates;
using uShareReal;
using agsXMPP.protocol.iq.disco;

public partial class MainWindow : Gtk.Window
{

	#region Private fields
	
	private XmppClientConnection Xmpp = null;
	private uShareReal.Objects.Account Account = null;
	
	private ImageMenuItem menuItemDisconnect = null;
	private ImageMenuItem menuItemQuit = null;
	private string menuTooltip = string.Empty;
	private uShareReal.UserWindow userWindow = null;
	private int connectCount = 0;

	public static DateTime connectedTime = DateTime.Now;
	
	#endregion
	
	#region Private methods
	
	private void LoadDefaultAccount()
	{
		if(Account != null)
			return; 
		
		FileStream stream = new FileStream(uShareReal.MainClass.PersonnalPath + "account.datas", FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
		if(stream != null)
		{
			if(stream.CanRead)
			{
				try
				{
					BinaryFormatter formatter = new BinaryFormatter();
					Account = formatter.Deserialize(stream) as uShareReal.Objects.Account;
				}
				catch { }
			}
			stream.Dispose();
		}
		stream = null;
		if(Account == null)
		{
			Account = new uShareReal.Objects.Account();
			Account.AutoConnect = false;
		}
	}
	
	private void SaveDefaultAccount()
	{
		if(Account == null)
			return;
		
		FileStream stream = new FileStream(uShareReal.MainClass.PersonnalPath + "account.datas", FileMode.Create, FileAccess.Write, FileShare.None);
		if(stream != null)
		{
			if(stream.CanWrite)
			{
				try
				{
					BinaryFormatter formatter = new BinaryFormatter();
					formatter.Serialize(stream, Account);
				}
				catch { }
			}
			stream.Dispose();
		}
		stream = null;
	}
	
	#endregion
	
	/// <summary>
	/// Main window.
	/// </summary>
	public MainWindow () : base(Gtk.WindowType.Toplevel)
	{
		LoadDefaultAccount();
		Build ();
		
		MainClass.TrayPopupMenu = new Menu();

		menuItemDisconnect = new ImageMenuItem (Catalog.GetString("Déconnecter..."));
		Gtk.Image appimg = new Gtk.Image(Stock.Disconnect, IconSize.Menu);
		menuItemDisconnect.Image = appimg;
		MainClass.TrayPopupMenu.Add(menuItemDisconnect);
		menuItemDisconnect.Activated += delegate { cancel_Clicked(this, new EventArgs()); };
		menuItemDisconnect.Sensitive = false;
		menuItemQuit = new ImageMenuItem (Catalog.GetString("Quitter."));
		appimg = new Gtk.Image(Stock.Quit, IconSize.Menu);
		menuItemQuit.Image = appimg;
		MainClass.TrayPopupMenu.Add(menuItemQuit);
		menuItemQuit.Activated += delegate { OnDeleteEvent(this, new DeleteEventArgs()); };
		checkbutton10.Active = MainClass.Config.TryToReconnect;

		Pixbuf trayIconFile = Pixbuf.LoadFromResource("iconDisabled.png");
		
		MainClass.TrayIcon = new StatusIcon(trayIconFile);
		MainClass.TrayIcon.Visible = true;
		MainClass.TrayIcon.Tooltip = this.Title;
		MainClass.TrayIcon.Activate += new EventHandler(trayClicked);
		MainClass.TrayIcon.PopupMenu += OnTrayIconPopup;
		MainClass.TrayNotification = new Notification(this.Title, Catalog.GetString("Bienvenue !"), MainClass.TrayIcon.Pixbuf, MainClass.TrayIcon);
		MainClass.TrayNotification.Urgency = Urgency.Normal;
		MainClass.TrayNotification.Category = Assembly.GetExecutingAssembly().GetName().Name;
		MainClass.TrayNotification.AttachToStatusIcon(MainClass.TrayIcon);
		if(MainClass.Config.ShowNotifyBaloons)
			MainClass.TrayNotification.Show();
		
		if(Xmpp == null)
			Xmpp = new XmppClientConnection(SocketConnectionType.Direct);
		
		Xmpp.OnAuthError += new XmppElementHandler(XmppAuthError);
		Xmpp.OnClose += new ObjectHandler(XmppClosed);
		Xmpp.OnError += new ErrorHandler(XmppError);
		Xmpp.OnLogin += new ObjectHandler(XmppLogin);
		Xmpp.OnSocketError += new ErrorHandler(XmppError);
		Xmpp.OnStreamError += new XmppElementHandler(XmppStreamError);
		Xmpp.OnXmppConnectionStateChanged += new XmppConnectionStateHandler(XmppStateChanged);
		Xmpp.OnReadXml += new XmlHandler(XmppReadXml);
		Xmpp.OnWriteXml += new XmlHandler(XmppWriteXml);
		Xmpp.ClientSocket.OnValidateCertificate += new System.Net.Security.RemoteCertificateValidationCallback(XmppCertificate);
		
		string username = null;
		try
		{
			username = new agsXMPP.Jid(Account.Name, Account.Host, Account.Resource).Bare;
		}
		catch { }
		entry1.Text = string.IsNullOrEmpty(username) ? string.Empty : username;
		entry2.Text = string.IsNullOrEmpty(Account.Password) ? string.Empty : Account.Password;
		entry3.Text = Account.Port.ToString();
		entry4.Text = string.IsNullOrEmpty(Account.Server) ? string.Empty : Account.Server;
		checkbutton3.Active = Account.UseSSL;
		checkbutton4.Active = Account.UseTLS;
		checkbutton5.Active = Account.UseCompression;
		checkbutton9.Active = Account.AutoConnect;
		
		if(Account.AutoConnect)
			connect_Clicked(this, new EventArgs());
		
	}

	protected void trayClicked(object sender, EventArgs a)
	{
		if(Xmpp.Authenticated && userWindow != null)
			userWindow.Visible = !userWindow.Visible;
		
		if(!Xmpp.Authenticated)
			this.Visible = !this.Visible;
			
	}
	
	protected void OnDeleteEventUser (object sender, DeleteEventArgs a)
	{
		SaveDefaultAccount();
		cancel_Clicked(sender, new EventArgs());
	}
	
	protected void OnDeleteEvent (object sender, DeleteEventArgs a)
	{
		MainClass.TrayNotification.Close();
		Application.Quit ();
		a.RetVal = true;
	}
	
	#region Xmpp events
	
	private bool XmppCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
	{   
		bool result = false;
		
		ManualResetEvent ev = new ManualResetEvent(false);
		
		Gtk.Application.Invoke(delegate {
			
		    if(MainClass.Config.IgnoreCertificates)
			{
				result = true;
				ev.Set();
				return;
			}
				
			if(MainClass.Config.IgnoreCertificateErrors && sslPolicyErrors != System.Net.Security.SslPolicyErrors.None)
			{
				result = true;
				ev.Set();
				return;
			}
			
			X509Certificate2 cert2 = new X509Certificate2(certificate.GetRawCertData());
			
			X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
			store.Open(OpenFlags.ReadWrite | OpenFlags.IncludeArchived);
			if(store.Certificates.Contains(cert2))
			{
				store.Close();
				result = true;
				ev.Set();
				return;
			}
			else
			{
				CertificateDialog cd = new CertificateDialog(cert2);
				ResponseType response = (ResponseType)cd.Run();
				
				if(cd.AutoAcceptForAll && response == ResponseType.Yes)
					MainClass.Config.IgnoreCertificates = true;
				
				if(cd.DontAskForThis && response == ResponseType.Yes)
					store.Add(cert2);
				
				store.Close();
				cd.Destroy();
				
				if(response == ResponseType.Yes)
					result = true;
				else
					result = false;

				ev.Set();
				return;
			}
		});
		
		ev.WaitOne();
		
		return result;
	}
	
	private void XmppReadXml(object sender, string xml)
	{
		if(MainClass.Config.DebugMode)
			Console.WriteLine("READ: " + xml);
		
	}
	
	private void XmppWriteXml(object sender, string xml)
	{
		if(MainClass.Config.DebugMode)
			Console.WriteLine("WRITE: " + xml);
		
	}
	
	private void XmppLogin(object sender)
	{
		Gtk.Application.Invoke(delegate {
			this.GdkWindow.Cursor = null;
			MainClass.TrayIcon.Blinking = false;
			MainClass.TrayIcon.Pixbuf = Pixbuf.LoadFromResource("icon.png");
			menuItemDisconnect.Sensitive = true;
			menuTooltip = this.Title + "\r\n(" + string.Format(Catalog.GetString("{0} connecté sur {1}"), Xmpp.MyJID.User, Xmpp.MyJID.Server) + ")";
			MainClass.TrayIcon.Tooltip = menuTooltip;
			MainClass.TrayNotification.Icon = MainClass.TrayIcon.Pixbuf;
			MainClass.TrayNotification.Urgency = Urgency.Critical;
			MainClass.TrayNotification.Body = string.Format(Catalog.GetString("{0} connecté sur {1}"), Xmpp.MyJID.User, Xmpp.MyJID.Server);
			if(MainClass.Config.ShowNotifyBaloons)
				MainClass.TrayNotification.Show();

			connectCount++;
			connectedTime = DateTime.Now;
			
			if(userWindow != null)
				userWindow.Destroy();
			
			userWindow = new UserWindow(Xmpp, Account);
			userWindow.DeleteEvent += new DeleteEventHandler(OnDeleteEventUser);
			
			this.Visible = false;
			userWindow.Show();
			userWindow.Present();
			
		});
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
		
		expander1.Expanded = false;
		SaveDefaultAccount();
	}	
	
	private void XmppStateChanged(object sender, XmppConnectionState state)
	{
		Gtk.Application.Invoke(delegate {
			string message = "<span font_weight='bold'>";
			switch (state)
			{
			case XmppConnectionState.Authenticating:
				message += Catalog.GetString("Authentification en cours...");
				break;
			case XmppConnectionState.Authenticated:
				message += Catalog.GetString("Authentifié.");
				break;
			case XmppConnectionState.Compressed:
				message += Catalog.GetString("Compression du flux activée.");
				break;
			case XmppConnectionState.Connected:
				message += Catalog.GetString("Connecté.");
				break;
			case XmppConnectionState.Connecting:
				message += Catalog.GetString("Connexion en cours...");
				break;
			case XmppConnectionState.Securing:
				message += Catalog.GetString("Sécurisation en cours...");
				break;
			case XmppConnectionState.SessionStarted:
				message += Catalog.GetString("Session démarée. Enjoy!");
				break;
			case XmppConnectionState.StartCompression:
				message += Catalog.GetString("Compression du flux.");
				break;
			}
			message += "</span>";
			label5.LabelProp = message;
		});
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
	}
	
	private void XmppAuthError(object sender, agsXMPP.Xml.Dom.Element e)
	{
		//Console.WriteLine("Xmpp authentification error:" + e.ToString());
		
		Gtk.Application.Invoke(delegate {
			button2.Sensitive = false;
			entry1.Sensitive = true;
			entry2.Sensitive = true;
			entry3.Sensitive = true;
			entry4.Sensitive = true;
			checkbutton3.Sensitive = true;
			checkbutton4.Sensitive = true;
			checkbutton5.Sensitive = true;
			checkbutton9.Sensitive = true;
			checkbutton10.Sensitive = true;
			button1.Sensitive = true;
			checkbutton9.Active = false;
			Account.AutoConnect = false;
			menuItemDisconnect.Sensitive = false;
			label5.LabelProp = "<span font_weight='bold'>" + Catalog.GetString("Erreur d'authentification.") + "</span>";
			this.GdkWindow.Cursor = null;
			MainClass.TrayIcon.Pixbuf = Pixbuf.LoadFromResource("iconDisabled.png");
			MainClass.TrayIcon.Blinking = false;
			MainClass.TrayIcon.Tooltip = this.Title + "\r\n" + Catalog.GetString("Erreur d'authentification.");
			MainClass.TrayNotification.Icon = MainClass.TrayIcon.Pixbuf;
			MainClass.TrayNotification.Urgency = Urgency.Critical;
			MainClass.TrayNotification.Body = Catalog.GetString("Erreur d'authentification.");
			
			if(userWindow != null)
				userWindow.Hide();
			
		});
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
	}
	
	private void XmppClosed(object sender)
	{
		Gtk.Application.Invoke(delegate {
			button2.Sensitive = false;
			label5.LabelProp = "<span font_weight='bold'>" + Catalog.GetString("Déconnecté.") + "</span>";
			entry1.Sensitive = true;
			entry2.Sensitive = true;
			entry3.Sensitive = true;
			entry4.Sensitive = true;
			checkbutton3.Sensitive = true;
			checkbutton4.Sensitive = true;
			checkbutton5.Sensitive = true;
			checkbutton9.Sensitive = true;
			checkbutton10.Sensitive = true;
			button1.Sensitive = true;
			menuItemDisconnect.Sensitive = false;
			this.GdkWindow.Cursor = null;
			this.Visible = true;
			MainClass.TrayIcon.Pixbuf = Pixbuf.LoadFromResource("iconDisabled.png");
			MainClass.TrayIcon.Blinking = false;
			MainClass.TrayIcon.Tooltip = this.Title;
			MainClass.TrayNotification.Icon = MainClass.TrayIcon.Pixbuf;
			MainClass.TrayNotification.Urgency = Urgency.Critical;
			MainClass.TrayNotification.Body = Catalog.GetString("Déconnecté.");
			if(MainClass.Config.ShowNotifyBaloons)
				MainClass.TrayNotification.Show();
			
			if(userWindow != null)
			{
				userWindow.OnPublicDelete();
				userWindow.Destroy();
			}
			
			this.Visible = true;
			this.Present();
			
		});
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
		
		if(MainClass.Config.TryToReconnect && connectCount < MainClass.Config.TryToReconnectCount)
			connect_Clicked(sender, new EventArgs());
		
	}
	
	private void XmppError(object sender, Exception e)
	{
		//Console.WriteLine("Xmpp error:" + e.ToString());
		if(e is System.Net.Sockets.SocketException)
		{
			Gtk.Application.Invoke(delegate {
				label5.LabelProp = "<span font_weight='bold'>" + Catalog.GetString("Erreur: ") + e.Message + "</span>";
			});
			
			while (Gtk.Application.EventsPending ())
	              Gtk.Application.RunIteration ();
			
		}
		if(e is IOException)
		{
			while(Xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
			{
				Xmpp.Close();
				
				while (Gtk.Application.EventsPending ())
	              Gtk.Application.RunIteration ();
				
			}
			XmppClosed(sender);
		
			checkbutton9.Active = false;
			Account.AutoConnect = false;
		}
		if(e is agsXMPP.net.ConnectTimeoutException)
		{
			Gtk.Application.Invoke(delegate {
				label5.LabelProp = "<span font_weight='bold'>" + Catalog.GetString("Erreur: Temps de connexion dépassé.") + "</span>";
			});
			
			while (Gtk.Application.EventsPending ())
				Gtk.Application.RunIteration ();

			Thread.Sleep(2000);
			
			while(Xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
			{
				Xmpp.Close();
				
				while (Gtk.Application.EventsPending ())
	              Gtk.Application.RunIteration ();
				
			}
			XmppClosed(sender);
		
			checkbutton9.Active = false;
			Account.AutoConnect = false;
		}
			
	}
	
	private void XmppStreamError(object sender, agsXMPP.Xml.Dom.Element e)
	{
		//Console.WriteLine("Xmpp Stream error:" + e.ToString());
	}
	
	#endregion
	
	#region Form events
	
	protected void OnTrayIconPopup (object o, EventArgs args) {
		MainClass.TrayPopupMenu.ShowAll();
		MainClass.TrayPopupMenu.Popup();
	}
	
	protected virtual void identity_Changed (object sender, System.EventArgs e)
	{
		agsXMPP.Jid jid = null;
		try
		{
			jid = new Jid(entry1.Text.Trim());
		}
		catch { }
		if(jid == null)
		{
			Account.Name = null;
			Account.Host = null;
			entry1.Text = string.Empty;
		}
		else
		{
			if(jid != null && jid.Bare != null)
			{
				Account.Name = string.IsNullOrEmpty(jid.User) ? null : jid.User.Trim();
				Account.Host = string.IsNullOrEmpty(jid.Server) ? null : jid.Server.Trim();
				entry1.Text = jid.Bare.Trim();
			}
			else
			{
				Account.Name = null;
				Account.Host = null;
				entry1.Text = string.Empty;
			}
		}
	}
	
	protected virtual void password_Changed (object o, Gtk.FocusOutEventArgs args)
	{
		if(string.IsNullOrEmpty(entry2.Text))
			Account.Password = null;
		else
			Account.Password = entry2.Text;
			
		entry2.Text = string.IsNullOrEmpty(Account.Password) ? string.Empty : Account.Password;
	}
	
	protected virtual void server_Changed (object o, Gtk.FocusOutEventArgs args)
	{
		if(string.IsNullOrEmpty(entry4.Text))
			Account.Server = null;
		else
			Account.Server = entry4.Text;
			
		entry4.Text = string.IsNullOrEmpty(Account.Server) ? string.Empty : Account.Server;
	}
	
	protected virtual void port_Changed (object o, Gtk.FocusOutEventArgs args)
	{
		int port;
		if(int.TryParse(entry3.Text, out port))
			Account.Port = port;
		
		entry3.Text = Account.Port.ToString();
	}
	
	protected virtual void ssl_Toggled (object sender, System.EventArgs e)
	{
		Account.UseSSL = checkbutton3.Active;
		checkbutton4.Active = Account.UseTLS;
		checkbutton5.Active = Account.UseCompression;
		entry3.Text = Account.Port.ToString();
	}
	
	protected virtual void tls_Toggled (object sender, System.EventArgs e)
	{
		Account.UseTLS = checkbutton4.Active;
		checkbutton3.Active = Account.UseSSL;
		checkbutton5.Active = Account.UseCompression;
		entry3.Text = Account.Port.ToString();
	}
	
	protected virtual void compression_Toggled (object sender, System.EventArgs e)
	{
		Account.UseCompression = checkbutton5.Active;
		checkbutton3.Active = Account.UseSSL;
		checkbutton4.Active = Account.UseTLS;
		entry3.Text = Account.Port.ToString();
	}
	
	protected virtual void autoconnect_Toggled (object sender, System.EventArgs e)
	{
		Account.AutoConnect = checkbutton9.Active;
	}
	
	protected virtual void autoreconnect_Toggled (object sender, System.EventArgs e)
	{
		MainClass.Config.TryToReconnect = checkbutton10.Active;
	}
	
	protected virtual void identity_Typed (object sender, System.EventArgs e)
	{
		button1.Sensitive = (!string.IsNullOrEmpty(entry1.Text.Trim()) && !string.IsNullOrEmpty(entry2.Text.Trim()));
	}
	
	protected virtual void password_Typed (object sender, System.EventArgs e)
	{
		button1.Sensitive = (!string.IsNullOrEmpty(entry1.Text.Trim()) && !string.IsNullOrEmpty(entry2.Text.Trim()));
	}
	
	protected virtual void connect_Clicked (object sender, System.EventArgs e)
	{
		if(Xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
			cancel_Clicked(this, e);

		Gtk.Application.Invoke(delegate {
			label5.LabelProp = "<span font_weight='bold'>" + Catalog.GetString("Lancement de la connexion...") + "</span>";
			button2.Sensitive = true;
			button1.Sensitive = false;
			entry1.Sensitive = false;
			entry2.Sensitive = false;
			entry3.Sensitive = false;
			entry4.Sensitive = false;
			checkbutton3.Sensitive = false;
			checkbutton4.Sensitive = false;
			checkbutton5.Sensitive = false;
			checkbutton9.Sensitive = false;
			checkbutton10.Sensitive = false;
			this.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Watch);
			MainClass.TrayIcon.Pixbuf = Pixbuf.LoadFromResource("icon.png");
			MainClass.TrayIcon.Blinking = true;
			MainClass.TrayIcon.Tooltip = this.Title;
			menuItemDisconnect.Sensitive = false;
		});
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
		
		Xmpp.AutoAgents = false;
		Xmpp.AutoPresence = false;
		Xmpp.AutoResolveConnectServer = (Account.Server == null);
		Xmpp.AutoRoster = false;
		Xmpp.ClientLanguage = Account.ClientLanguage;
		Xmpp.ClientVersion = Account.ClientVersion;
		if(!string.IsNullOrEmpty(Account.Server))
			Xmpp.ConnectServer = Account.Server;
		
		Xmpp.Show = ShowType.NONE;
		Xmpp.EnableCapabilities = true;
		Xmpp.KeepAlive = true;
		Xmpp.Port = Account.Port;
		Xmpp.Priority = Account.Priority;
		Xmpp.Server = string.IsNullOrEmpty(Account.Host) ? string.Empty : Account.Host;
		Xmpp.Status = Account.Status;
		Xmpp.UseCompression = Account.UseCompression;
		Xmpp.UseSSL = Account.UseSSL;
		Xmpp.UseStartTLS = Account.UseTLS;
		
		Xmpp.Capabilities.Node = "http://uShareReal.com/caps";
		Xmpp.Capabilities.AddExtension("share");
		
		Xmpp.DiscoInfo.AddIdentity(new DiscoIdentity("pc", "uShareReal", "client"));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_INFO));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.IQ_TIME));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.IQ_VERSION));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.PING));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.VCARD));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.VCARD_UPDATE));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.CHATSTATES));
		Xmpp.DiscoInfo.AddFeature(new DiscoFeature("http://uShareReal.com/sharing"));

		Xmpp.Open(Account.Name, Account.Password, Account.Resource);
		
		while (Gtk.Application.EventsPending ())
              Gtk.Application.RunIteration ();
		
	}
	
	protected virtual void cancel_Clicked (object sender, System.EventArgs e)
	{
		if(Xmpp != null && Xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
			Xmpp.Close();
		else
			XmppClosed(sender);
		
		checkbutton9.Active = false;
		Account.AutoConnect = false;
	}
	
	#endregion
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}

